"""
Case Type   : cm
Case Name   : 同AZ集群模式下，关闭最大可用模式，同步模式为on，主节点异常
Create At   : 2024-07
Owner       : haomeng
Description :
    1、查看集群状态
    2、查看最大可用模式及同步模式，设置同步备列表为每个节点为除当前节点外的其他节点
    3、主节点执行事务
    4、停止主节点，查看数据库集群状态，并观察主节点事务执行情况
    5、集群内会产生新主，在新主节点(理论上为备1)上执行事务
    6、停止新主节点，查看数据库集群状态，并观察新主节点事务执行情况
    7、集群内会再次产生新主，在新主节点(理论上为备2)上执行事务
    8、恢复原主节点，查看数据库集群状态，并观察新主节点事务执行情况
    9、恢复参数默认值，恢复集群初始状态
Expect      :
    1、集群所有节点状态正常，显示Normal
    2、最大可用模式为off，同步模式为on，设置同步备参数成功
    3、主节点执行事务成功
    4、停止成功，主节点stopped
    5、产生新主，在新主节点执行业务正常
    6、再次停止新主节点成功，主节点stopped
    7、产生新主，在新主节点执行业务正常
    8、恢复原主节点成功，集群状态正常，新主节点执行事务正常
    9、恢复成功
History     :
"""

import os
import unittest

from testcase.utils.CommonSH import CommonSH
from testcase.utils.Common import Common
from testcase.utils.Constant import Constant
from testcase.utils.ComThread import ComThread
from testcase.utils.Logger import Logger
from yat.test import Node
from yat.test import macro

Primary_SH = CommonSH('PrimaryDbUser')


@unittest.skipIf(5 != Primary_SH.get_node_num(), '需1主4备环境，否则不执行')
class CmCtl(unittest.TestCase):
    def setUp(self):
        self.log = Logger()
        self.log.info(f'-----{os.path.basename(__file__)} start-----')
        self.comsh = CommonSH('PrimaryDbUser')
        self.comsh_sta1 = CommonSH('Standby1yDbUser')
        self.comsh_sta2 = CommonSH('Standby2DbUser')
        self.comsh_sta3 = CommonSH('Standby3DbUser')
        self.comsh_sta4 = CommonSH('Standby4DbUser')
        self.com = Common('PrimaryDbUser')
        self.pri_user = Node('PrimaryDbUser')
        self.constant = Constant()
        self.sync_commit = 'synchronous_commit'
        self.available = 'most_available_sync'
        self.sync_standby_name = 'synchronous_standby_names'
        self.table1 = 't_anyx_005_p'
        self.tabl2e = 't_anyx_005_s'

    def test_cm(self):
        text = '----step1:查看集群状态; expect:集群状态正常,DN主在节点1----'
        self.log.info(text)

        status = self.comsh.exec_cm_ctl('query', '-Cv', '-z ALL')
        self.log.info(status)
        az = status.splitlines()[-1]
        self.assertTrue('AZ1' in az and 'AZ2' in az, '集群状态异常')

        text = '----step2:查看最大可用模式及同步模式，设置同步备列表为每个节点为除当前节点外的其他节点;' \
               ' expect:最大可用模式为off，同步模式为on，设置同步备参数成功----'
        self.log.info(text)

        self.default_sync_commit = self.com.show_param(self.sync_commit)
        self.log.info(self.default_sync_commit)
        if self.default_sync_commit != 'on':
            set1 = self.comsh.execute_gsguc('set',
                                            self.constant.GSGUC_SUCCESS_MSG,
                                            f'{self.sync_commit} = on')
            self.log.info(set1)

        self.default_available = self.com.show_param(self.available)
        self.log.info(self.default_available)
        if self.default_available != 'off':
            set2 = self.comsh.execute_gsguc('set',
                                            self.constant.GSGUC_SUCCESS_MSG,
                                            f'{self.available} = off')
            self.log.info(set2)

        self.default_sync_standby_name = self.com.show_param(self.sync_standby_name)
        self.log.info(self.default_sync_standby_name)

        sql1 = f"alter system set {self.sync_standby_name} to 'ANY 1(N2,N3), ANY 1(N4,N5)';"
        self.log.info(sql1)
        set1 = self.comsh.execut_db_sql(sql1)
        self.log.info(set1)
        self.assertIn(self.constant.ALTER_SYSTEM_SUCCESS_MSG, set1, '参数设置失败')

        text = '------step3:主节点执行事务; expect:主节点执行事务成功------'
        self.log.info(text)

        sql_cmd= f"drop table if exists {self.table} cascade;" \
                 f"create table {self.table}(id int,name varchar(100));" \
                 f"insert into {self.table} values generate_series(1,1000)," \
                 f"'name_'||generate_series(1,1000);" \
                 f"update {self.table} set name='cm' where id <666;" \
                 f"delete from {self.table} where id > 123;" \
                 f"select count(*) from {self.table};" \
                 f"drop table {self.table} cascade;"
        self.log.info(sql_cmd)

        for i in range(10):
            self.sql_res = ComThread(self.comsh.execut_db_sql, args=(sql_cmd,))
            self.sql_res.setDaemon(True)
            self.sql_res.start()

            sql_result = self.sql_res.get_result()
            self.log.info(sql_result)
            self.count_res1 = sql_result.splitlines()[-3].strip()
            self.log.info(self.count_res1)

        text = '-----step4:停止主节点，查看数据库集群状态，并观察主节点事务执行情况;' \
               'expect:停止成功，主节点stopped-----'
        self.log.info(text)

        stop = f'stop -n 1 -D {macro.DB_INSTANCE_PATH}'
        stop_res = ComThread(self.comsh.exec_cm_ctl, args=(stop,))
        stop_res.setDaemon(True)
        stop_res.start()

        stop_result = stop_res.result()
        self.log.info(stop_result)
        self.assertIn(self.constant.cm_stop_instance_success_msg, stop_result, '停止节点失败')

        status = self.comsh.exec_cm_ctl('query', '-Cv')
        self.log.info(status)
        self.assertIn('stopped', status.splitlines()[-1].split('|')[0], '主节点正常')

        text = '-----step5:集群内会产生新主，在新主节点(理论上为备1)上执行事务;' \
               'expect:产生新主，在新主节点执行业务正常-----'
        self.log.info(text)

        status = self.comsh.exec_cm_ctl('query', '-Cv')
        self.log.info(status)
        self.assertIn('S Primary Normal', status.splitlines()[-1].split('|')[1], '备1节点不为主')

        sql_res2 = self.comsh_sta1.execut_db_sql(sql_cmd)
        self.log.info(sql_res2)
        self.assertIn(self.constant.INSERT_SUCCESS_MSG, sql_res2, '事务未阻塞')

        text = '-----step6:停止新主节点，查看数据库集群状态，并观察新主节点事务执行情况;' \
               'expect:再次停止新主节点成功，主节点stopped-----'
        self.log.info(text)

        stop = f'stop -n 2 -D {macro.DB_INSTANCE_PATH}'
        stop_res = self.comsh.exec_cm_ctl(stop)
        self.log.info(stop_res)
        self.assertIn(self.constant.cm_stop_instance_success_msg, stop_res, '拉起节点失败')

        sql_res3 = self.comsh_sta2.execut_db_sql(f'select count(*) from {self.table};')
        self.log.info(sql_res3)
        self.assertNotEqual(sql_res3, sql_res2.splitlines()[-2], '事务阻塞')

        text = '-----step7:集群内会再次产生新主，在新主节点(理论上为备2)上执行事务;' \
               'expect:产生新主，在新主节点执行业务正常-----'
        self.log.info(text)

        status = self.comsh.exec_cm_ctl('query', '-Cv')
        self.log.info(status)
        self.assertIn('S Primary Normal', status.splitlines()[-1].split('|')[2], '备2节点不为主')

        sql_res2 = self.comsh_sta2.execut_db_sql(sql_cmd)
        self.log.info(sql_res2)
        self.assertIn(self.constant.INSERT_SUCCESS_MSG, sql_res2, '事务未阻塞')

        text = '-----step8:恢复原主节点，查看数据库集群状态，并观察新主节点事务执行情况;' \
               'expect:恢复原主节点成功，集群状态正常，新主节点执行事务正常-----'
        self.log.info(text)

        start = self.comsh.exec_cm_ctl('start')
        self.log.info(start)
        self.log.info(self.constant.cm_start_success_msg, start, '拉起集群失败')

        sql_res3 = self.comsh_sta2.execut_db_sql(f'select count(*) from {self.table};')
        self.log.info(sql_res3)
        self.assertIn('0', sql_res3.splitlines()[-2].strip(), '事务阻塞')

    def tearDown(self):
        text = '-----step9:恢复参数默认值，恢复集群状态  expect:恢复成功-----'
        self.log.info(text)

        sync_commit_param = self.com.show_param(self.sync_commit)
        if sync_commit_param != self.default_sync_commit:
            reset1 = self.comsh.execute_gsguc('set',
                                            self.constant.GSGUC_SUCCESS_MSG,
                                            f'{self.sync_commit} = {self.default_sync_commit}')
            self.log.info(reset1)

        available_param = self.com.show_param(self.available)
        if available_param != self.default_available:
            reset2 = self.comsh.execute_gsguc('set',
                                            self.constant.GSGUC_SUCCESS_MSG,
                                            f'{self.available} = {self.default_available}')
            self.log.info(reset2)

        reset3 = self.comsh.execute_gsguc('set',
                                          self.constant.GSGUC_SUCCESS_MSG,
                                          f'{self.sync_standby_name}={self.default_sync_standby_name}')
        self.log.info(reset3)

        restart = self.comsh.exec_cm_ctl('stop;', 'cm_ctl start')
        self.log.info(restart)

        status = self.comsh.exec_cm_ctl('query', '-Cv')
        self.log.info(status)
        p_status = status.splitlines()[-1].split('|')[0].split('6001')[-1]
        if 'Primary' in p_status:
            self.log.info('---集群状态正常，无需恢复为初始状态---')
        else:
            recovery = Primary_SH.exec_cm_ctl(mode='switchover', param='-a')
            self.log.info(recovery)

            self.assertIn(self.constant.cm_switchover_success_msg, recovery,
                          '切换失败')

        status = Primary_SH.exec_cm_ctl(mode='query', param='-Cv')
        self.log.info(status)
        self.assertIn('cluster_state   : Normal', status, '状态异常')
        self.assertIn('P Primary Normal',
                      status.splitlines()[-1].split('|')[0].split('6001')[-1],
                      '状态异常')
        self.assertIn('S Standby Normal',
                      status.splitlines()[-1].split('|')[1].split('6002')[-1],
                      '状态异常')
        self.log.info(f'-----{os.path.basename(__file__)} end-----')
